﻿// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/KismetStringLibrary.h"
#include "Kismet/KismetMathLibrary.h"
#include "UObject/Object.h"
#include "FlyweightPattern.generated.h"

/**	享元模式 Flyweight Pattern
 *	描述
 *		运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象，而这些对象都很相似，状态变化很小，可以实现对象的多次复用。
 *		由于享元模式要求能够共享的对象必须是细粒度对象，因此它又称为轻量级模式，它是一种对象结构型模式。
 *		享元类可以有多个对象，单例类只有一个对象
 *		内部状态
 *			享元对象内部不随外界环境改变而改变的共享部分。
 *		外部状态
 *			享元对象内部随环境的改变而改变，不能够共享的状态就是外部状态。
 *	套路
 *		Flyweight（抽象享元类）
 *			一个接口或抽象类，声明了具体享元类的公共方法。
 *		ConcreteFlyweight（具体享元类）
 *			实现了抽象享元类，其实例称为享元对象。
 *		UnsharedConcreteFlyweight（非共享具体享元类）
 *			并不是所有的抽象享元类的子类都需要被共享，不能被共享的子类可设计为非共享具体享元类。
 *		FlyweightFactory（享元工厂类）
 *			用于创建并管理享元对象，一般设计为一个存储“Key-Value”键值对的集合（可以结合工厂模式设计）。其作用就在于：提供一个用于存储享元对象的享元池，当用户需要对象时，首先从享元池中获取，如果享元池中不存在，那么则创建一个新的享元对象返回给用户，并在享元池中保存该新增对象
 *	使用场景
 *		程序需要生成数量巨大的相似对象
 *		这将耗尽目标设备的所有内存
 *		对象中包含可抽取且能在多个对象间共享的重复状态。
 *		示例
 *			游戏场景中的植被、子弹等重复的物体，贴图或是网格一样，Transform不一样
 *			注意与对象池（也叫资源池，如连接池、线程池）的区别，对象池可重复使用，每次使用都是独占使用；享元是共享使用；对象池可以看作是享元模式的升级版
 *	优缺点
 *		优点
 *			享元模式的优点在于它可以极大减少内存中对象的数量，使得相同对象或相似对象在内存中只保存一份。
 *			享元模式的外部状态相对独立，而且不会影响其内部状态，从而使得享元对象可以在不同的环境中被共享。
 *		缺点
 *			享元模式使得系统更加复杂，需要分离出内部状态和外部状态，这使得程序的逻辑复杂化。
 *			为了使对象可以共享，享元模式需要将享元对象的状态外部化，而读取外部状态使得运行时间变长。
 **/

// 抽象享元类 
UCLASS(Abstract)
class MISCELLANEOUSPROJECT_API AFlyweightObject : public AActor
{
	GENERATED_BODY()
public:
	// 用于每次重新调用时，初始化各个参数
	virtual void Init(FVector pInitPos)
	{
		bIsLiving = true;
		// UE_LOG(LogTemp, Warning, TEXT("----------------------------------"));
		// UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s is used. Pos%s"), *this->GetName(), *UKismetStringLibrary::Conv_VectorToString(pInitPos));

		GetWorld()->GetTimerManager().SetTimer(m_pTimerHandle, this, &AFlyweightObject::End, 1.0f, false);
	}

	// 自定义销毁程序，不是真正的销毁实例
	virtual void End()
	{
		if (bIsLiving)
		{
			// UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" ******** %s is destroyed."), *this->GetName());
			bIsLiving = false;
			GetWorld()->GetTimerManager().ClearTimer(m_pTimerHandle);
		}
	}

	// 击中
	virtual void Hit() PURE_VIRTUAL(AFlyweightObject::IsValid,);

	// 用于判断是否已经在使用、空闲状态
	bool IsLiving()
	{
		return bIsLiving;
	}

protected:
	bool bIsLiving;
	FTimerHandle m_pTimerHandle;
};

// 具体享元类 —— 地鼠
UCLASS()
class MISCELLANEOUSPROJECT_API AMole : public AFlyweightObject
{
	GENERATED_BODY()
public:

	virtual void Hit()
	{
		// UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s is hit. Add 10 score"), *this->GetName());
		End();
	}
};

// 具体享元类 —— 炸弹
UCLASS()
class MISCELLANEOUSPROJECT_API ABomb : public AFlyweightObject
{
	GENERATED_BODY()
public:
	virtual void Hit()
	{
		// UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s is hit. Add -5 score"), *this->GetName());
		End();
	}
};

// 享元工厂类 
UCLASS()
class MISCELLANEOUSPROJECT_API AFlyweightFactory : public AActor
{
	GENERATED_BODY()
public:
	// 初始化多个地鼠和炸弹实例以供使用
	void Init()
	{
		for (int32 i = 0; i < 3; i++)
		{
			AMole* mole = GetWorld()->SpawnActor<AMole>(FVector::ZeroVector, FRotator::ZeroRotator);
			m_pMolePool.Add(mole->GetName(), mole);

			ABomb* bomb = GetWorld()->SpawnActor<ABomb>(FVector::ZeroVector, FRotator::ZeroRotator);
			m_pBombPool.Add(bomb->GetName(), bomb);
		}
	}

	// 获取未被使用的地鼠实例
	AMole* GetMole()
	{
		for (TTuple<FString, AMole*>& item : m_pMolePool)
		{
			if (!(item.Value)->IsLiving())
			{
				return item.Value;
			}
		}
		return nullptr;
	}

	// 获取未被使用的炸弹实例
	ABomb* GetBomb()
	{
		for (TTuple<FString, ABomb*>& item : m_pBombPool)
		{
			if (!(item.Value)->IsLiving())
			{
				return item.Value;
			}
		}
		return nullptr;
	}

	// 随机获取未被使用的地鼠或炸弹实例
	AFlyweightObject* GetRandomElement()
	{
		if (UKismetMathLibrary::RandomFloat() < 0.7f)
		{
			return GetMole();
		} else
		{
			return GetBomb();
		}
	}

private:
	// 存储的地鼠对象池
	UPROPERTY()
	TMap<FString, AMole*> m_pMolePool;

	// 存储的炸弹对象池
	UPROPERTY()
	TMap<FString, ABomb*> m_pBombPool;
};

UCLASS()
class MISCELLANEOUSPROJECT_API AFlyweightTestActor : public AActor
{
	GENERATED_BODY()
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	UPROPERTY()
	AFlyweightFactory* m_pFlyweightFactory;

	UPROPERTY()
	FTimerHandle TimerHandle;

	// 定时生成地鼠或炸弹
	UFUNCTION()
	void AutoSpawner();
};
